package com.log.demo.aop;

import com.log.demo.constant.MarkConstant;
import com.log.demo.util.JsonUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * <T>User：chk</T>
 * <T>Description：web请求日志切面</T>
 * <T>Update record(who,time,message):</T>
 */
@Aspect
@Component
public class ApiRequestLogAspect {

    //日志缓存列表
    private List<String> logList;

    //默认打印的响应结果的最大长度
    private static final int DEFAULT_RETURN_LEN = 2048;

    //controller类末尾字符串
    private static final String CONTROLLER_END = "Controller";


    //日志句柄
    private static Logger logger = LoggerFactory.getLogger(ApiRequestLogAspect.class);

    private final String controllerExecution = "execution(* com.log.demo.controller..*.*(..))";

    private final String serviceExecution = "execution(* com.log.demo.service..*.*(..))";

    private final String daoExecution = "execution(* com.log.demo.dao..*.*(..))";


    /**
     * controller切点
     */
    @Pointcut(controllerExecution)
    public void controllerCut() {

    }

    /**
     * service切点
     */
    @Pointcut(serviceExecution)
    public void serviceCut() {

    }

    /**
     * dao切点
     */
    @Pointcut(daoExecution)
    public void daoCut() {

    }

    /*
     * 执行controller方法之前
     * @param joinPoint
     */
    @Before("controllerCut()")
    public void doControllerBefore(JoinPoint joinPoint) {
        logList = new ArrayList<>();
        String currentTime = getCurrentTime();
        saveLogList(MarkConstant.ENTER);
        saveLogList("******************请求开始*******************");
        saveLogList("开始时间：" + currentTime);
        //此处IP可以根据自己需要获取
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("IP:" + "127.0.0.1"));
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("Controller:" + getFullMethodName(joinPoint)));
        String args = ArrayUtils.toString(joinPoint.getArgs());

        //过滤掉包含密码字样的参数打出
        if(!args.contains("password")){
            saveLogList(currentTime + MarkConstant.DOT + addBrackets("Controller参数：" + args));
        }
    }

    /*
     * 执行service方法之前
     * @param joinPoint
     */
    @Before("serviceCut()")
    public void doServiceBefore(JoinPoint joinPoint) {
        String currentTime = getCurrentTime();
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("Service:" + getFullMethodName(joinPoint)));
        String args = ArrayUtils.toString(joinPoint.getArgs());
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("Service参数：" + args));
    }

    /*
     * 执行dao方法之前
     * @param joinPoint
     */
    @Before("daoCut()")
    public void doDAOBefore(JoinPoint joinPoint) {
        String currentTime = getCurrentTime();
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("DAO：" + getFullMethodName(joinPoint)));
        String args = ArrayUtils.toString(joinPoint.getArgs());
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("DAO：" + args));
    }

    /*
     * 定义方法正常返回的后置处理
     * @param rvt
     */
    @AfterReturning(returning = "rvt", pointcut = "controllerCut()")
    public void afterReturn(JoinPoint joinPoint, Object rvt) {
        //controller方法执行方法返回事件  其他暂时不执行
        String className = getSimpleClassName(joinPoint);
        String currentTime = getCurrentTime();
        saveLogList(currentTime + MarkConstant.DOT + addBrackets("Controller返回值：" + returnToString(rvt)));
        saveLogList("结束时间：" + currentTime);
        saveLogList("******************请求结束*******************");
        printLog(false);
    }

    /*
     * 结果转字符串
     * @param rvt
     * @return
     */
    private String returnToString(Object rvt) {
        String jsonResult;
        if (rvt instanceof List) {
            jsonResult = JsonUtils.toArrayJson((List)rvt);
        } else if (rvt instanceof Serializable) {
            //后期若需要在打印日志的时候，过滤敏感词，可以在这边处理
            jsonResult = JsonUtils.toJson(rvt);
        } else {
            jsonResult = rvt + "";
        }
        if (StringUtils.isNotBlank(jsonResult) && jsonResult.length() > DEFAULT_RETURN_LEN) {
            jsonResult = jsonResult.substring(0, DEFAULT_RETURN_LEN);
        }
        return jsonResult;
    }

    /*
     * 当方法抛出异常时，提供异常处理
     * @param tw
     */
    @AfterThrowing(throwing = "tw", pointcut = "controllerCut()")
    public void afterThrow(JoinPoint joinPoint, Throwable tw) {
        //controller方法执行方法返回事件  其他暂时不执行
        saveLogList("结束时间：" + getCurrentTime());
        saveLogList("=========访问结束================");
        saveLogList(getStackTrace(tw));
        printLog(true);
    }

    /*
     * 保存日志信息
     * @param log 要保存的日志信息
     */
    private void saveLogList(String log){
        if(null != logList){
            logList.add(log);
        }
    }

    /*
     * 打印日志
     */
    private void printLog(boolean isError){
        try{
            if(!CollectionUtils.isEmpty(logList)){
                StringBuilder info = new StringBuilder();
                logList.forEach(logStr -> info.append(logStr).append("\n"));
                if(isError){
                    logger.error(info.toString());
                }else{
                    logger.info(info.toString());
                }
            }
            logList = null;
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /*
     * 获取请求
     * @return
     */
    private HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    /*
     * 获取当前时间
     * @return
     */
    private String getCurrentTime() {
        String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        return addBrackets(currentTime);
    }

    /*
     * 添加中括号
     * @param value
     * @return
     */
    private String addBrackets(String value) {
        return MarkConstant.LEFT_BRACKET + value + MarkConstant.RIGHT_BRACKET;
    }


    /*
     * 获取完整的方法名
     * @param joinPoint
     * @return
     */
    private String getFullMethodName(JoinPoint joinPoint){
        return joinPoint.getTarget().getClass().getName() + MarkConstant.DOT + joinPoint.getSignature().getName();
    }

    /*
     * 获取类名
     * @param joinPoint
     * @return
     */
    private String getSimpleClassName(JoinPoint joinPoint) {
        return joinPoint.getTarget().getClass().getSimpleName();
    }

    /*
     * 获取异常的堆栈信息
     * @param t
     * @return
     */
    private static String getStackTrace(Throwable t) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        try {
            t.printStackTrace(pw);
            return sw.toString();
        } finally{
            pw.close();
        }
    }
}
